package com.guigu.guliproduct.service.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guigu.gulicommon.constant.enumes.ware.HasStock;
import com.guigu.gulicommon.entity.pojo.AttrsPoJo;
import com.guigu.gulicommon.entity.pojo.res.Res;
import com.guigu.gulicommon.entity.vo.HasStockSkuVo;
import com.guigu.gulicommon.entity.vo.SpuInfoVo;
import com.guigu.gulicommon.entity.vo.es.SkuEsModel;
import com.guigu.gulicommon.exception.BaseException;
import com.guigu.guliproduct.entity.*;
import com.guigu.guliproduct.entity.dto.InsertSpuInfoBatchDto;
import com.guigu.guliproduct.feign.SearchServer;
import com.guigu.guliproduct.feign.WareServer;
import com.guigu.guliproduct.mapper.*;
import com.guigu.guliproduct.service.ISpuInfoService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.guigu.gulicommon.constant.DefaultConstant.DEFAULT_HOT_SCORE;
import static com.guigu.gulicommon.constant.enumes.product.PublishStatus.PUBLISHED;
import static com.guigu.gulicommon.constant.enumes.product.SearchType.YES;
import static com.guigu.gulicommon.constant.enumes.res.AddResCode.INSERT_SPU_SUCCESS;
import static com.guigu.gulicommon.constant.enumes.res.SelectResCode.*;
import static com.guigu.gulicommon.constant.enumes.res.ServerResCode.SERVER_BUSY;
import static com.guigu.gulicommon.constant.enumes.res.UpdateResCode.UPDATE_SPU_PUBLISHED_SUCCESS;
import static com.guigu.gulicommon.constant.enumes.ware.HasStock.NO;

/**
 * <p>
 * spu信息 服务实现类
 * </p>
 *
 * @author 陈俊杰
 * @since 2024-07-13
 */
@Service
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoMapper, SpuInfo> implements ISpuInfoService {
    @Autowired
    private SkuInfoMapper skuInfoMapper;
    @Autowired
    private WareServer wareServer;
    @Autowired
    private ProductAttrValueMapper productAttrValueMapper;
    @Autowired
    private AttrMapper attrMapper;
    @Autowired
    private SearchServer searchServer;
    @Autowired
    private BrandMapper brandMapper;
    @Autowired
    private CategoryMapper categoryMapper;

    /**
     * 批量新增商品信息(通用)
     *
     * @param dto 添加商品信息的参数
     * @return vo 添加商品信息的信息
     */
    @Override
    public Res<SpuInfo> addBatch(ArrayList<InsertSpuInfoBatchDto> dto) {
        return null;
    }

    /**
     * 商品上架
     *
     * @param spuId 商品id
     * @return 商品信息
     */
    @Override
    public Res<SkuEsModel> up(Long spuId) {
        // 1. 根据spuId查询商品sku信息和当前sku可以被用来检索的规格属性
        // 1.1 查询sku信息
        List<SkuInfo> skuInfos = new LambdaQueryChainWrapper<>(skuInfoMapper).eq(SkuInfo::getSpuId, spuId).list();
        if (skuInfos.isEmpty()) {
            throw new BaseException(SELECT_SKU_FILE_NOT_EXIST);
        }
        // 1.2 遍历所有skuId成为集合
        List<Long> skuIds = skuInfos.stream().map(SkuInfo::getSkuId).collect(Collectors.toList());
        // 1.3 远程调用查询库存
        Res<HasStockSkuVo> wmsWareSkuVoRes;
        try {
            wmsWareSkuVoRes = wareServer.getStockBySkuIds(skuIds);
        } catch (Exception e) {
            log.error("远程调用[仓储服务]失败,原因如下----------->", e);
            throw new BaseException(SERVER_BUSY);
        }
        // 1.4 验证远程调用结果
        if (!wmsWareSkuVoRes.getCode().equals(SELECT_SKU_STOCK_SUCCESS.getCode())) {
            throw new BaseException(wmsWareSkuVoRes.getCode(), wmsWareSkuVoRes.getMsg());
        }
        // 1.5 获取远程调用返回的数据列表,并封装为Map
        Map<Long, HasStock> stockMap = wmsWareSkuVoRes.getData().stream()
                .collect(Collectors.toMap(HasStockSkuVo::getSkuId, HasStockSkuVo::getHasStock,
                        // 这里定义如何处理重复的键
                        (existing, replacement) -> {
                            //如果两个的HasStockSkuVo::getHasStock有一个为YES,则返回YES
                            return HasStock.YES.equals(existing) || HasStock.YES.equals(replacement) ? HasStock.YES : NO;
                        }
                ));
        // 1.6 查询当前sku可以被用来检索的规格属性
        // 1.6.1 查询对应属性集合
        List<ProductAttrValue> baseAttrs = new LambdaQueryChainWrapper<>(productAttrValueMapper)
                .eq(ProductAttrValue::getSpuId, spuId)
                .list();
        // 1.6.2 提取属性ID列表
        List<Long> attrIds = baseAttrs.stream().map(ProductAttrValue::getAttrId).collect(Collectors.toList());
        // 1.6.3 获取可以被用来检索的属性ID集合
        HashSet<Long> idSet = new LambdaQueryChainWrapper<>(attrMapper)
                .in(Attr::getAttrId, attrIds)
                .eq(Attr::getSearchType, YES)
                .list().stream().map(Attr::getAttrId)
                .collect(Collectors.toCollection(HashSet::new));
        // 1.6.4 构建SkuEsModel.Attrs对象列表
        List<AttrsPoJo> attrsList = baseAttrs.stream()
                .filter(item -> idSet.contains(item.getAttrId()))
                .map(item -> new AttrsPoJo()
                        .setAttrId(item.getAttrId())
                        .setAttrName(item.getAttrName())
                        .setAttrValue(item.getAttrValue()))
                .collect(Collectors.toList());
        // 2. 封装返回数据
        //2.3 查询商品品牌和分类信息
        Brand brand = brandMapper.selectById(skuInfos.get(0).getBrandId());
        Category category = categoryMapper.selectById(skuInfos.get(0).getCatelogId());

        List<SkuEsModel> esModelList = skuInfos.stream().map(skuInfo -> {
            // 2.1 拷贝数据
            SkuEsModel skuEsModel = new SkuEsModel();
            BeanUtils.copyProperties(skuInfo, skuEsModel);
            skuEsModel.setBrandName(brand.getName()).setCatalogName(category.getName());
            // 2.2 设置默认图片和价格
            skuEsModel.setSkuImg(skuInfo.getSkuDefaultImg())
                    .setCatalogId(skuInfo.getCatelogId())
                    .setSkuPrice(skuInfo.getPrice())
                    // 2.3 设置库存信息
                    .setHasStock(stockMap.get(skuInfo.getSkuId()) == null ? NO : stockMap.get(skuInfo.getSkuId()))
                    // 2.4 设置评分
                    .setHotScore(DEFAULT_HOT_SCORE)
                    // 2.5 设置检索属性:品牌和名字信息
                    .setAttrs(attrsList)
                    // 2.6 设置品牌图片
                    .setBrandImg(brand.getLogo());
            ;
            return skuEsModel;
        }).collect(Collectors.toList());

        // 3. 把商品信息保存到ES中
        Res<SkuEsModel> skuEsModelRes;
        try {
            //3.1 远程调用检索服务, 保存商品信息到ES
            skuEsModelRes = searchServer.productStatusUp(esModelList);
        } catch (Exception e) {
            log.error("远程调用[检索服务]失败,原因如下----------->", e);
            throw new BaseException(SERVER_BUSY);
        }
        // 3.2 验证远程调用结果
        if (!skuEsModelRes.getCode().equals(INSERT_SPU_SUCCESS.getCode())) {
            //TODO 重复调用产生的接口幂等性,重试机制问题需要解决

            // 3.2.2 如果失败,抛出异常
            throw new BaseException(skuEsModelRes.getCode(), skuEsModelRes.getMsg());
        }
        //4 修改数据库中商品状态为上架
        SpuInfo spuInfo = new SpuInfo().setId(spuId).setPublishStatus(PUBLISHED);
        this.updateById(spuInfo);
        // 返回封装后的商品信息
        return new Res<>(UPDATE_SPU_PUBLISHED_SUCCESS, esModelList);
    }


    /**
     * 根据skuId得到spu信息
     *
     * @param skuIds skuId集合
     * @return spu信息
     */
    @Override
    public Res<SpuInfoVo> getSpuInfoBySkuId(List<Long> skuIds) {
        List<SkuInfo> skuInfos = skuInfoMapper.selectBatchIds(skuIds);
        List<Long> spuIds = skuInfos.stream().map(SkuInfo::getSpuId).distinct().collect(Collectors.toList());
        List<SpuInfoVo> spuInfoVos = this.listByIds(spuIds).stream().map(spuInfo -> {
            SpuInfoVo spuInfoVo = new SpuInfoVo();
            BeanUtils.copyProperties(spuInfo, spuInfoVo);
            return spuInfoVo;
        }).collect(Collectors.toList());
        return new Res<>(SELECT_SPU_INFO_SUCCESS, spuInfoVos);
    }
}
